scrapboxwatcher.py 1
文脈:
READMEなど読者向けの解説はありません
コード
code:py
import os
import re
import sys
import tweepy
import feedparser
def file2str(filepath):
ret = ''
with open(filepath, encoding='utf8', mode='r') as f:
ret = f.read()
return ret
def str2file(filepath, s):
with open(filepath, encoding='utf8', mode='w') as f:
f.write(s)
def file2list(filepath):
ret = []
with open(filepath, encoding='utf8', mode='r') as f:
return ret
def list2file(filepath, ls):
with open(filepath, encoding='utf8', mode='w') as f:
def get_rss_entries():
feed = feedparser.parse(url)
return feed.entries
def post_tweet(tweet):
# twitter_token.bat をつくって実行して埋めろ
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.Client(
bearer_token=None,
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token=access_token,
access_token_secret=access_token_secret,
)
api.create_tweet(text=tweet)
def remove_html_tags(text):
clean = re.compile('<.*?>')
return re.sub(clean, '', text)
def clean_rss_summary_for_tweet(rss_summary):
s = rss_summary
s = remove_html_tags(s)
# 本当は改行で上手く整形したかったが、rss summary から \n が見えないことがあってどのみちできない
# 単純に n-space で区切るくらいでいいや
s = s.replace(' ', ' ')
s = s.replace('sta.icon', '')
# URLが途中で切れると以下になる
# tweepy.errors.BadRequest: 400 Bad Request / The Tweet contains an invalid URL.
# url部分だけ省く、とかは正規表現的にだるそうなので、もう機械的にプロトコル文字列除くくらいにする
# 140文字制限だが、140は長すぎる
# きりのよさから64が思いついた
# あまり長くなさそうだが、tweetにみっちり文章入れるのも汚いのでこれでいいかなと
LENGTH = 64
if len(cut_for_tweet)>=LENGTH:
cut_for_tweet = f'{cut_for_tweet}...'
return cut_for_tweet
def clean_rss_title_for_tweet(rss_title):
s = rss_title
DUSTTAIL_LENGTH = len(' - stakiran研究所 - Scrapbox')
return s
class StringOuter:
def __init__(self, filename):
self._filename = filename
self._s = ''
def add(self, s):
self._s = s
def append(self, s):
self._s = f'{self._s}\n\n{s}'
def out(self):
str2file(self._filename, self._s)
class DuplicateGuard:
def __init__(self, filepath):
self._filepath = filepath
self._lines = []
self._new_if_doesnot_exist()
self._load()
def _load(self):
self._lines = file2list(self._filepath)
def _new_if_doesnot_exist(self):
if os.path.exists(self._filepath):
return
self.save()
def save(self):
list2file(self._filepath, self._lines)
def is_found(self, pagename):
for line in self._lines:
if pagename==line:
return True
return False
def add(self, pagename):
if self.is_found(pagename):
return
self._lines.append(pagename)
MYFULLPATH = os.path.abspath(sys.argv0) MYDIR = os.path.dirname(MYFULLPATH)
DUPULICATE_GUARD_FILE = 'scrapboxwatcher_dup.md'
DUPULICATE_GUARD_FULL = os.path.join(MYDIR, DUPULICATE_GUARD_FILE)
guard = DuplicateGuard(DUPULICATE_GUARD_FULL)
entries = get_rss_entries()
target_entries = []
for entry in entries:
title = clean_rss_title_for_tweet(entry.title)
if guard.is_found(title):
continue
target_entries.append(entry)
outer = StringOuter('out_scrapboxwatcher1.md')
for entry in target_entries:
title = clean_rss_title_for_tweet(entry.title)
summary = clean_rss_summary_for_tweet(entry.summary)
url = entry.link
tweet = f'''{title}
{summary}
{url}'''
post_tweet(tweet)
outer.append(tweet)
guard.add(title)
guard.save()
outer.out()
雑多に
これは何?
細かい実装の話
RSSで取ってくる部分は最新30件固定
つまり「/staから直近30件を取ってきて、まだ投稿されてない分を全部投稿する」スクリ 一度投稿したページ名は内部で記録し、二度以上投稿されないようにする
DuplicateGuard
ツイートの構成
URLだけだと検索してもヒットしないのでタイトルをつけた
タイトルだけだと内容わからないので、本文もとりあえず先頭64文字だけ載せてみた
StringOuterはただのデバッグ
開発画面
https://gyazo.com/de84b8b67c65d395be834f91652680d7
VSCode便利だsta.icon
運用どうしてる?
空いた時にでも手で叩くつもり
python scrapboxwatcher.py